--[[
	Author: another
	
	Custom flyout buttons support
]]
	
local ARROW_BUTTON_DEF_HEIGHT, ARROW_BUTTON_MIN_HEIGHT, ARROW_BUTTON_MAX_HEIGHT = 15, 5, 36
local BF_SETTINGS_GROUP = "Button Facade"


--variables
local AddonName, AddonTable = ...
FbcButtonFrames = {}
FbcArrowButtons = {}

FbcSettingsMode = nil
FbcKeybindMode = nil
FbcArrowButtonsHeight = nil
FbcShowBorders = nil
FbcEnableMouseover = nil
FbcEnableMouseoverInCombat = nil
FbcHideOnClick = nil
FbcHighlight = nil
FbcRightClickSelfCast = nil
FbcRightClickSet = nil
FbcStubFrame = CreateFrame("Frame", nil, UIParent, "SecureHandlerBaseTemplate")

local EventHandlersTable = {}
local EnteringWorldOrVariablesLoaded = 0
local UpdateMacroCount = 0
--Button Facade stuff
local LBF
local BF_Table = {}
FbcLBFMasterGroup = nil

local FbcSettingsWindow
local FBC_SETTINGS_CHECKBOX, FBC_SETTINGS_SLIDER = 1, 2


--[[structures
ActionButton
	- customFlyout
	ArrowBtn
		- FlyoutArrow		
		- expandDir: up, left, down, right
		- expanded
	
		- FlyoutListFrame	--also through SetFrameRef
			- ButtonList
]]

local function RegisterFrames()
	FbcButtonFrames = {}
	local frame = EnumerateFrames()
	while frame do
		if frame.IsProtected and frame.GetObjectType and frame.GetScript and frame:GetObjectType() == "CheckButton" and frame:IsProtected() then
			local script = frame:GetScript("OnClick")
			if script == ActionButton1:GetScript("OnClick") or tostring(frame:GetName()):match("^BT4Button%d+$") then
				tinsert(FbcButtonFrames, frame)
			end
		end
		frame = EnumerateFrames(frame)
	end
end

local function AttachToAllActionButtons()
	for _, frame in ipairs(FbcButtonFrames) do
		if frame.action or frame._state_action then
			FlyoutArrowButton_Attach(frame)
		end
	end
end

local function ShowAllFlyoutButtons()
	for _, frame in ipairs(FbcButtonFrames) do
		if frame.action or frame._state_action then
			local arrowBtn = FbcArrowButtons[frame:GetName()]
			if arrowBtn then 
				arrowBtn:ShowArrow()
			end
		end
	end
end

local function HideUnusedFlyoutButtons()
	for _, frame in ipairs(FbcButtonFrames) do
		if frame.action or frame._state_action then
			local arrowBtn = FbcArrowButtons[frame:GetName()]
			if arrowBtn then 
				arrowBtn:UpdateArrow()
				arrowBtn:HideArrow()
			end
		end
	end
end

local function HideFlyoutFrames()
	for _, frame in ipairs(FbcButtonFrames) do
		if frame.action or frame._state_action then
			if frame and not(InCombatLockdown()) then
				local arrowBtn = FbcArrowButtons[frame:GetName()]
				if (arrowBtn) then 
					arrowBtn:SetAttribute("expanded", false)
					arrowBtn.FlyoutListFrame:Hide()
				end
			end
		end
	end
end


--[[ Button Facade stuff ]]

function FlyoutButtonCustom_ButtonFacadeCallback(self, SkinID, Gloss, Backdrop, Group, Button, Colors)
	-- If no group is specified, save the data as the root add-on skin.
	-- This will allow the ButtonFacade GUI to display it correctly.
	if not(Group) then
		FlyoutButtonCustom_Settings[BF_SETTINGS_GROUP]["SkinID"] = SkinID
		FlyoutButtonCustom_Settings[BF_SETTINGS_GROUP]["Gloss"] = Gloss
		FlyoutButtonCustom_Settings[BF_SETTINGS_GROUP]["Backdrop"] = Backdrop
		FlyoutButtonCustom_Settings[BF_SETTINGS_GROUP]["Colors"] = Colors
	else
		FlyoutButtonCustom_Settings[BF_SETTINGS_GROUP][Group]["SkinID"] = SkinID
		FlyoutButtonCustom_Settings[BF_SETTINGS_GROUP][Group]["Gloss"] = Gloss
		FlyoutButtonCustom_Settings[BF_SETTINGS_GROUP][Group]["Backdrop"] = Backdrop
		FlyoutButtonCustom_Settings[BF_SETTINGS_GROUP][Group]["Colors"] = Colors
	end
end


--[[ events stuff ]]

local EventFrame = CreateFrame("Frame")
EventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
EventFrame:RegisterEvent("VARIABLES_LOADED")
EventFrame:RegisterEvent("UPDATE_MACROS")

function EventFrame:LIBKEYBOUND_ENABLED()
	FbcKeybindMode = true
end

function EventFrame:LIBKEYBOUND_DISABLED()
	FbcKeybindMode = nil
end

function EventFrame:LIBKEYBOUND_MODE_COLOR_CHANGED()
	print("color changed")
end


--event handlers
local function DoUpdates(func)
	for _, arrowBtn in pairs(FbcArrowButtons) do
		for i, v in ipairs(arrowBtn.FlyoutListFrame.ButtonList) do
			if v.command then
				func(v)
			end
		end
	end
end

local function EventEnteringCombat(self, ...)
	FbcSettingsMode = false
end

local function EventTalentGroupChanged(self, ...)
	if InCombatLockdown() then
		return
	end
	
	for _, arrowBtn in pairs(FbcArrowButtons) do
		for i, v in ipairs(arrowBtn.FlyoutListFrame.ButtonList) do
			v.command, v.value, v.subValue = nil, nil, nil
		end
	end

	HideFlyoutFrames()
	FbcArrowButtons = {}
	RegisterFrames()
	AttachToAllActionButtons()
	
	DoUpdates(FlyoutListButton.SetTooltip)
	DoUpdates(FlyoutListButton.UpdateTexture)
	DoUpdates(FlyoutListButton.UpdateCooldown)
	DoUpdates(FlyoutListButton.UpdateChecked)
	DoUpdates(FlyoutListButton.UpdateEquipped)
	DoUpdates(FlyoutListButton.UpdateUsable)
	DoUpdates(FlyoutListButton.UpdateButtonText)
end

local function EventLearnedSpellInTab(self, ...)
	DoUpdates(FlyoutListButton.SetTooltip)
	DoUpdates(FlyoutListButton.UpdateTexture)
	DoUpdates(FlyoutListButton.UpdateCooldown)
	DoUpdates(FlyoutListButton.UpdateChecked)
	DoUpdates(FlyoutListButton.UpdateUsable)
	DoUpdates(FlyoutListButton.UpdateButtonText)
end

local function RegisterEvents()
	--event table
	EventHandlersTable = {
		--common
		["PLAYER_REGEN_DISABLED"] 			= EventEnteringCombat,
		["ACTIVE_TALENT_GROUP_CHANGED"] 	= EventTalentGroupChanged,
		["LEARNED_SPELL_IN_TAB"] 			= EventLearnedSpellInTab,
	}
	
	EventFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
	EventFrame:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED")
	EventFrame:RegisterEvent("LEARNED_SPELL_IN_TAB")
end


--[[ slash handlers ]]

local function SlashTable_border(rest)
	FbcShowBorders = not(FbcShowBorders)
	FlyoutButtonCustom_Settings["ShowBorders"] = FbcShowBorders
	print("ShowBorders = "..tostring(FbcShowBorders))

	for _, arrowBtn in pairs(FbcArrowButtons) do
		local flf = arrowBtn.FlyoutListFrame
		for i, v in ipairs(arrowBtn.FlyoutListFrame.ButtonList) do
			if v.command then
				v:UpdateTexture()
			end
		end
	end
end

local function SlashTable_hide(rest)
	FbcHideOnClick = not(FbcHideOnClick)
	FlyoutButtonCustom_Settings["HideOnClick"] = FbcHideOnClick
	print("HideOnClick = "..tostring(FbcHideOnClick))

	for _, arrowBtn in pairs(FbcArrowButtons) do
		for _, v in ipairs(arrowBtn.FlyoutListFrame.ButtonList) do
			v:SetHideOnClick(FbcHideOnClick)
		end
	end
end

local function SlashTable_highlight(rest)
	FbcHighlight = not(FbcHighlight)
	FlyoutButtonCustom_Settings["Highlight"] = FbcHighlight
	print("Highlight = "..tostring(FbcHighlight))

	for _, arrowBtn in pairs(FbcArrowButtons) do
		arrowBtn:SetHighlight(FbcHighlight)
	end
end

local function SlashTable_height(rest)
	if rest ~= "" then
		local temp = tonumber(rest)
		if not(temp) or (temp < ARROW_BUTTON_MIN_HEIGHT) or (temp > ARROW_BUTTON_MAX_HEIGHT) then
			print("new height value '"..rest.."' out of range "..ARROW_BUTTON_MIN_HEIGHT.."-"..ARROW_BUTTON_MAX_HEIGHT)
		else
			FbcArrowButtonsHeight = temp
			FlyoutButtonCustom_Settings["ArrowButtonsHeight"] = FbcArrowButtonsHeight
			
			for _, arrowBtn in pairs(FbcArrowButtons) do
				arrowBtn:SetFrameSize(arrowBtn:GetAttribute("expandDir"))
			end
		end
	end
end

local function SlashTable_keybind(rest)
	FbcLibKeyBound:Toggle()
end

local function SlashTable_mouseover(rest)
	if InCombatLockdown() then
		print("can't toggle mouseover while in combat")
		return
	end
	
	FbcEnableMouseover = not(FbcEnableMouseover)
	FlyoutButtonCustom_Settings["EnableMouseover"] = FbcEnableMouseover
	print("EnableMouseover = "..tostring(FbcEnableMouseover))
	for _, arrowBtn in pairs(FbcArrowButtons) do
		if FbcEnableMouseover then
			arrowBtn:SetAttribute("_onenter", ArrowButtonOnEnterSnippet)
		else
			arrowBtn:SetAttribute("_onenter", "")
		end
	end
end

local function SlashTable_mouseoverincombat(rest)
	if InCombatLockdown() then
		print("can't toggle mouseoverincombat while in combat")
		return
	end
	
	FbcEnableMouseoverInCombat = not(FbcEnableMouseoverInCombat)
	FlyoutButtonCustom_Settings["EnableMouseoverInCombat"] = FbcEnableMouseoverInCombat
	print("EnableMouseoverInCombat = "..tostring(FbcEnableMouseoverInCombat))
	for _, arrowBtn in pairs(FbcArrowButtons) do
		arrowBtn:SetAttribute("mouseoverincombat", FbcEnableMouseoverInCombat)
	end
end

local function SlashTable_settings(rest)
	if InCombatLockdown() then
		print("can't enter SettingsMode while in combat")
		return
	end
	
	FbcSettingsMode = not(FbcSettingsMode)
	print("SettingsMode = "..tostring(FbcSettingsMode))
	if FbcSettingsMode then
		EventTalentGroupChanged()
		ShowAllFlyoutButtons()
	else
		HideUnusedFlyoutButtons()
	end
end

local function SlashTable_unique(rest)
	if InCombatLockdown() then
		print("can't set unique while in combat")
		return
	end
	
	FbcStubFrame:SetAttribute("unique-list", not(FbcStubFrame:GetAttribute("unique-list")))
	FlyoutButtonCustom_Settings["UniqueList"] = FbcStubFrame:GetAttribute("unique-list")
	print("unique list = "..tostring(FbcStubFrame:GetAttribute("unique-list")))
end

local function SlashTable_rightClickSelfCast(rest)
	FbcRightClickSelfCast = not(FbcRightClickSelfCast)
	FlyoutButtonCustom_Settings["RightClickSelfCast"] = FbcRightClickSelfCast
	print("right-click self cast = "..tostring(FbcRightClickSelfCast))

	for _, arrowBtn in pairs(FbcArrowButtons) do
		for _, v in ipairs(arrowBtn.FlyoutListFrame.ButtonList) do
			v:SetRightClickSelfCast(FbcRightClickSelfCast)
		end
	end
end

local function SlashTable_rightClickSet(rest)
	FbcRightClickSet = not(FbcRightClickSet)
	FlyoutButtonCustom_Settings["RightClickSet"] = FbcRightClickSet
	print("right-click set = "..tostring(FbcRightClickSet))

	for _, arrowBtn in pairs(FbcArrowButtons) do
		for _, v in ipairs(arrowBtn.FlyoutListFrame.ButtonList) do
			v:SetRightClickSelfCast(FbcRightClickSet)
		end
	end
end

local FlyoutButtonCustom_SlashTable = {
	{
		['name'] = 'border',
		['hint'] = "toggle button borders",
		['func'] = SlashTable_border,
		['get'] = function() return FbcShowBorders; end,
		['type'] = FBC_SETTINGS_CHECKBOX
	},
	{
		['name'] = 'hide',
		['hint'] = "toggle hiding on list button click",
		['func'] = SlashTable_hide,
		['get'] = function() return FbcHideOnClick; end,
		['type'] = FBC_SETTINGS_CHECKBOX
	},
	{
		['name'] = 'highlight',
		['hint'] = "toggle highlight on arrow mouseover",
		['func'] = SlashTable_highlight,
		['get'] = function() return FbcHighlight; end,
		['type'] = FBC_SETTINGS_CHECKBOX
	},
	{
		['name'] = 'kb',
		['hint'] = "toggle keybinding mode",
		['func'] = SlashTable_keybind,
		['get'] = function() return FbcKeybindMode; end,
		['type'] = FBC_SETTINGS_CHECKBOX
	},
	{
		['name'] = 'mouseover',
		['hint'] = "toggle opening/hiding lists on arrow button mouseover",
		['func'] = SlashTable_mouseover,
		['get'] = function() return FbcEnableMouseover; end,
		['type'] = FBC_SETTINGS_CHECKBOX
	},
	{
		['name'] = 'mouseoverincombat',
		['hint'] = "toggle enable/disable mouseover in combat",
		['func'] = SlashTable_mouseoverincombat,
		['get'] = function() return FbcEnableMouseoverInCombat; end,
		['type'] = FBC_SETTINGS_CHECKBOX
	},
	{
		['name'] = 'settings',
		['hint'] = "toggle SettingsMode",
		['func'] = SlashTable_settings,
		['get'] = function() return FbcSettingsMode; end,
		['type'] = FBC_SETTINGS_CHECKBOX
	},
	{
		['name'] = 'unique',
		['hint'] = "toggle only 1 list visible",
		['func'] = SlashTable_unique,
		['get'] = function() return FlyoutButtonCustom_Settings["UniqueList"]; end,
		['type'] = FBC_SETTINGS_CHECKBOX
	},
	{
		['name'] = 'right-click self cast',
		['hint'] = "toggle right-click self cast",
		['func'] = SlashTable_rightClickSelfCast,
		['get'] = function() return FbcRightClickSelfCast; end,
		['type'] = FBC_SETTINGS_CHECKBOX
	},
	{
		['name'] = 'right-click set button',
		['hint'] = "toggle right-click set bar button out of combat",
		['func'] = SlashTable_rightClickSet,
		['get'] = function() return FbcRightClickSet; end,
		['type'] = FBC_SETTINGS_CHECKBOX
	},
	{
		['name'] = 'height',
		['hint'] = " set arrow button height, values 5-36",
		['func'] = SlashTable_height,
		['get'] = function() return FbcArrowButtonsHeight; end,
		['type'] = FBC_SETTINGS_SLIDER
	},
}

local function FlyoutButtonCustom_SlashHandler(msg, editbox)
	local command, rest = msg:match("^(%S*)%s*(.-)$")
	-- Any leading non-whitespace is captured into command
	-- the rest (minus leading whitespace) is captured into rest.
	command = string.lower(command)
	
	if command == "" then
		FbcSettingsWindow:Show()
	elseif command == "help" then
		print("FlyoutButton Custom slash commands:\n/fbc or /fbcustom, params:")
		for _, v in ipairs(FlyoutButtonCustom_SlashTable) do
			print(v.name.." : "..v.hint)
		end
	else
		local found = false
		for _, v in ipairs(FlyoutButtonCustom_SlashTable) do
			if v.name == command then
				found = true
				v.func(rest)
				break
			end
		end
		
		if not(found) then
			print("unknown command '"..command.."'")
		end
	end
end

local spb_frame = SpellBookFrame
spb_frame:HookScript("OnShow", function()
	if not(InCombatLockdown()) then
		FbcSettingsMode = true
		EventTalentGroupChanged()
		ShowAllFlyoutButtons()
	end
end)
spb_frame:HookScript("OnHide", function()
	FbcSettingsMode = false
	if not(InCombatLockdown()) then
		HideUnusedFlyoutButtons()
	end
end)

local function GetKeyValue(key, ...)
	if FlyoutButtonCustom_Settings[key] ~= nil then
		return FlyoutButtonCustom_Settings[key]
	else
		return ...
	end
end

local function Init()
	FlyoutButtonCustom_Settings = FlyoutButtonCustom_Settings or {}
	local fbc_s = FlyoutButtonCustom_Settings
	
	--read settings
	FbcArrowButtonsHeight 		= GetKeyValue("ArrowButtonsHeight", ARROW_BUTTON_DEF_HEIGHT)
	FbcShowBorders 				= GetKeyValue("ShowBorders", true)
	FbcEnableMouseover			= GetKeyValue("EnableMouseover", false)
	FbcEnableMouseoverInCombat 	= GetKeyValue("EnableMouseoverInCombat", true)
	FbcHideOnClick 				= GetKeyValue("HideOnClick", true)
	FbcHighlight 				= GetKeyValue("Highlight", true)
	FbcRightClickSelfCast		= GetKeyValue("RightClickSelfCast", true)
	FbcRightClickSet			= GetKeyValue("RightClickSet", true)
	if fbc_s["UniqueList"] == nil then
		fbc_s["UniqueList"] = true
	end
	FbcStubFrame:SetAttribute("unique-list", fbc_s["UniqueList"])
	FlyoutButtonCustom_Settings["expandDir"] = FlyoutButtonCustom_Settings["expandDir"] or {}
	--for 2 talent sets
	FlyoutButtonCustom_Settings["expandDir"][1] = FlyoutButtonCustom_Settings["expandDir"][1] or {}
	FlyoutButtonCustom_Settings["expandDir"][2] = FlyoutButtonCustom_Settings["expandDir"][2] or {}
	
	--Button Facade
	if (LibStub) then
		LBF = LibStub("LibButtonFacade", true)
		if (LBF) then
			FbcLBFMasterGroup = LBF:Group("FlyoutButtonCustom")
			LBF:RegisterSkinCallback("FlyoutButtonCustom", FlyoutButtonCustom_ButtonFacadeCallback, BF_Table)
		end
	end
	FlyoutButtonCustom_Settings[BF_SETTINGS_GROUP] = FlyoutButtonCustom_Settings[BF_SETTINGS_GROUP] or {}
	if (FbcLBFMasterGroup and fbc_s[BF_SETTINGS_GROUP]["SkinID"]) then
		FbcLBFMasterGroup:Skin(fbc_s[BF_SETTINGS_GROUP]["SkinID"], fbc_s[BF_SETTINGS_GROUP]["Gloss"], fbc_s[BF_SETTINGS_GROUP]["Backdrop"], fbc_s[BF_SETTINGS_GROUP]["Colors"])
	end			
	
	RegisterFrames()
	AttachToAllActionButtons()
	RegisterEvents()
	
	--LibKeyBound
	if (LibStub) then
		FbcLibKeyBound = LibStub("LibKeyBound-1.0")
		if (FbcLibKeyBound) then
			FbcLibKeyBound.RegisterCallback(EventFrame, "LIBKEYBOUND_ENABLED")
			FbcLibKeyBound.RegisterCallback(EventFrame, "LIBKEYBOUND_DISABLED")
			FbcLibKeyBound.RegisterCallback(EventFrame, "LIBKEYBOUND_MODE_COLOR_CHANGED")
		end
	end

	SLASH_FBCUSTOM1, SLASH_FBCUSTOM2 = "/fbc", "/fbcustom"
	SlashCmdList["FBCUSTOM"] = FlyoutButtonCustom_SlashHandler
	
	--SettingsWindow
	FbcSettingsWindow = CreateFrame("Frame", "FBCSettingsDialog", UIParent, "FBCSettingsDialogTemplate")
	for i, v in ipairs(FlyoutButtonCustom_SlashTable) do
		local name = "FlyoutButtonCustom_SettingsButton_"..v.name
		if v.type == FBC_SETTINGS_CHECKBOX then
			v.widget = CreateFrame("CheckButton", name, FbcSettingsWindow, "FBCSettingsButtonTemplate")
			local btn = v.widget
			_G[name.."Text"]:SetText(v.name)
			btn.tooltipText = v.hint
			btn.func = v.func
			btn.get = v.get
			btn.type = v.type
			btn:SetPoint("TOPLEFT", FbcSettingsWindow, "TOPLEFT", 80, -60 - i * 25)
		elseif v.type == FBC_SETTINGS_SLIDER then
			v.widget = CreateFrame("Slider", name, FbcSettingsWindow, "OptionsSliderTemplate")
			local slider = v.widget

			local subText = slider:CreateFontString(name.."Value", "ARTWORK", "GameFontHighlight")
			subText:SetPoint("CENTER", slider, "CENTER", 0, -12)
			
			_G[name.."Text"]:SetText(v.name)
			_G[name.."Low"]:SetText(ARROW_BUTTON_MIN_HEIGHT)
			_G[name.."High"]:SetText(ARROW_BUTTON_MAX_HEIGHT)
			slider.valueText = _G[name.."Value"]
			slider:SetValueStep(1)
			slider:SetMinMaxValues(ARROW_BUTTON_MIN_HEIGHT, ARROW_BUTTON_MAX_HEIGHT)
			slider.tooltipText = v.hint
			slider.func = v.func
			slider.get = v.get
			slider.type = v.type
			slider:SetPoint("TOPLEFT", FbcSettingsWindow, "TOPLEFT", 80, -350)
			slider:SetScript("OnValueChanged", function(self) self.valueText:SetText(self:GetValue()); end)
		end
	end	
end

function FBCSettingsDialogTemplate_OnShow(self)
	local buttons = { self:GetChildren() }
	for i, b in ipairs(buttons) do
		if b.get then
			b.old = b.get()
			if b.type == FBC_SETTINGS_CHECKBOX then
				b:SetChecked(b.get())
			elseif b.type == FBC_SETTINGS_SLIDER then
				b:SetValue(b.get())
				b.valueText:SetText(b.get())
			end
		end
	end
end

local function toboolean(v)
	return not not v
end

function FBCSettingsDialogTemplate_Apply(self)
	local buttons = { self:GetChildren() }
	for i, b in ipairs(buttons) do
		if b.func then
			if b.type == FBC_SETTINGS_CHECKBOX then
				if toboolean(b.old) ~= toboolean(b:GetChecked()) then
					b.func()
				end
			elseif b.type == FBC_SETTINGS_SLIDER then
				local value = b:GetValue()
				if b.old ~= value then
					b.func(value)
				end
			end
		end
	end
end

local function OnEvent(self, event, ...)
	if EventHandlersTable[event] then
		EventHandlersTable[event](self, ...)
	elseif event == "PLAYER_ENTERING_WORLD" or event == "VARIABLES_LOADED" or event == "UPDATE_MACROS" then
		EnteringWorldOrVariablesLoaded = EnteringWorldOrVariablesLoaded + 1
		if event == "PLAYER_ENTERING_WORLD" then EventFrame:UnregisterEvent("PLAYER_ENTERING_WORLD") end
		if event == "VARIABLES_LOADED" then EventFrame:UnregisterEvent("VARIABLES_LOADED") end
		if event == "UPDATE_MACROS" then
			--local numglobal, numperchar = GetNumMacros()
			--print('UpdateMacroCount = '..UpdateMacroCount..', numglobal = '..numglobal..', numperchar = '..numperchar)
			UpdateMacroCount = UpdateMacroCount + 1
			if UpdateMacroCount > 1 then
				EventFrame:UnregisterEvent("UPDATE_MACROS")
			end
		end
		if EnteringWorldOrVariablesLoaded > 3 then
			Init()
		end
	end
end

EventFrame:SetScript("OnEvent", OnEvent)
